package play.coroutine.basics

import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import cz.msebera.android.httpclient.client.methods.CloseableHttpResponse
import cz.msebera.android.httpclient.client.methods.HttpGet
import cz.msebera.android.httpclient.client.methods.HttpPost
import cz.msebera.android.httpclient.entity.ByteArrayEntity
import cz.msebera.android.httpclient.entity.ContentType
import cz.msebera.android.httpclient.impl.client.HttpClientBuilder
import cz.msebera.android.httpclient.util.EntityUtils
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.URL
import java.nio.channels.Channels
import java.time.LocalDateTime

fun test() = runBlocking {
    val url = getDownloadAddress("test-otdr-portable-server2")

    val a1 = async {
        try {
            val file = File("app1.apk")
            println("下载app1.apk...")
            withContext(Dispatchers.IO) {
                println("start download...")
                url.openStream()
            }.use {
                Channels.newChannel(it).use { rbc ->
                    println("start download2...")
                    FileOutputStream(file).use { fos -> fos.channel.transferFrom(rbc, 0, Long.MAX_VALUE) }
                    println("download success ${file.length() / 1024 / 1024}")
                }
            }
        } catch (e: Exception) {
            println("download error: $e")
        }
    }

    val a2 = async {
        try {
            println("下载app2.apk...")
            val data = withContext(Dispatchers.IO) {
                url.openConnection().getInputStream().readAllBytes()
            }
            val file = File("app2.apk")
            file.writeBytes(data)
            println("download2 success ${file.length() / 1024 / 1024}")

        } catch (e: IOException) {
            println(e.message)
        }
    }
    val a3 = async { downloadApp("test-otdr-portable-server2", "APP3", File("app3.apk")) }
    val a4 = async { downloadApp2("test-otdr-portable-server2", "APP4", File("app4.apk")) }
    a1.await()
    a2.await()
    a3.await()
    a4.await()

    println("下载完成...")
}

private fun getDownloadAddress(tag: String): URL {
    return URL("http://res.furthersense.cn/api/v1/upgrade/download/${tag}/latest")
}

suspend fun downloadApp(tag: String, name: String, file: File): Boolean {
    val url = getDownloadAddress(tag)
    val channel = Channel<Boolean>()
    CoroutineScope(Dispatchers.IO).launch {
        var count = 1
        while (isActive) {
            try {
                withContext(Dispatchers.IO) {
                    i("[下载download]", "开始下载<$name>,下载地址: $url")
                    url.openStream()
                }.use {
                    Channels.newChannel(it).use { rbc ->
                        FileOutputStream(file).use { fos -> fos.channel.transferFrom(rbc, 0, Long.MAX_VALUE) }
                    }
                    i("[下载download]", "下载<${name}>完成,大小: ${file.length() / 1024 / 1024}M")
                    channel.send(true)
                    this.cancel()
                }
            } catch (e: Exception) {
                count++
                e("[下载download]", "下载<${name}>失败:${e.message}")
                delay(3000)
                if (count > 10) {
                    e("[下载download]", "下载<${name}>失败次数超过10次,请重启")
                    channel.send(false)
                    this.cancel()
                }
            }
        }
    }
    return channel.receive()
}


suspend fun downloadApp2(tag: String, name: String, file: File): Boolean {
    val url = getDownloadAddress(tag)
    val channel = Channel<Boolean>()
    val httpGet = HttpGet(url.toURI())
    val httpClient = HttpClientBuilder.create().build()
    CoroutineScope(Dispatchers.IO).launch {
        var count = 1
        while (isActive) {
            try {
                withContext(Dispatchers.IO) {
                    i("[下载download]", "开始下载<$name>,下载地址: $url")
                    val response: CloseableHttpResponse = httpClient.execute(httpGet)
                    file.writeBytes(EntityUtils.toByteArray(response.entity))
                    i("[下载download]", "下载<${name}>完成,大小: ${file.length() / 1024 / 1024}M")
                    channel.send(true)
                }
            } catch (e: Exception) {
                count++
                e("[下载download]", "下载<${name}>失败:${e.message}")
                delay(3000)
                if (count > 10) {
                    e("[下载download]", "下载<${name}>失败次数超过10次,请重启")
                    channel.send(false)
                    this.cancel()
                }
            }
        }
    }
    return channel.receive()
}

fun e(tag: String, msg: String) {
    println("Error $tag $msg")
}


fun i(tag: String, msg: String) {
    println("INFO  $tag $msg")
}

fun testHttpClient() {
    try {
        val response = HttpClientBuilder.create().build().execute(HttpGet("http://res.furthersense.cn/api/v1/upgrade/download/otdr-upgrade-manager-release0/latest"))
        println(response.entity.contentLength)
        val file = File("2.txt")
        file.writeBytes(response.entity.content.readBytes())
        println("${response.entity.contentLength} ${file.readBytes().size}")
    } catch (e: Exception) {
        println(e.message)
    }
}


fun testHttpClientPost() {
    data class Log(val msg: String, val at: String) {
        @SerializedName("device_id")
        lateinit var deviceId: String

        constructor(msg: String, at: String, deviceId: String) : this(msg, at) {
            this.deviceId = deviceId
        }
    }

    val post = HttpPost("http://yzkj.mynetgear.com:7000/api/upgrade-log")
    post.entity = ByteArrayEntity(
        Gson().toJson(Log("ok", LocalDateTime.now().toString(), "null")).toByteArray(),
        ContentType.APPLICATION_JSON
    )
    val response = HttpClientBuilder.create().build().execute(post)
    println(response.entity.contentLength)
    println(EntityUtils.toString(response.entity))
}

fun main() {
//    testHttpClientPost()
    testHttpClient()
}